package cool.taomu.toolkit.lmdb

import com.google.inject.Inject
import com.google.inject.name.Named
import cool.taomu.toolkit.inter.IStoreKeyValue
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.util.Map
import org.lmdbjava.Dbi
import org.lmdbjava.DbiFlags
import org.lmdbjava.Env
import org.lmdbjava.GetOp
import org.slf4j.LoggerFactory

class LmdbStore implements IStoreKeyValue<String, byte[]> {
	
	val static LOG = LoggerFactory.getLogger(LmdbStore)

	Env<ByteBuffer> env;

	Dbi<ByteBuffer> db;

	@Inject(optional=true)
	@Named("taomu.lmdb.id")
	String identifier = "taomu_lmdb_default_store";

	@Inject
	new(Env<ByteBuffer> env) {
		this.env = env;
		this.db = env.openDbi(identifier, DbiFlags.MDB_CREATE);
	}

	override putIfAbsent(String key, byte[] value) {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override remove(Object key, Object value) {
		var keyByte = (key as String).getBytes(StandardCharsets.UTF_8);
		var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
		keyBuf.put(keyByte).flip
		var valueStr = value as String;
		var ByteBuffer valueBuf = ByteBuffer.allocateDirect(valueStr.length);
		valueBuf.put(valueStr.getBytes(StandardCharsets.UTF_8)).flip
		try (var txn = env.txnWrite()) {
			var boolean result = db.delete(txn, keyBuf, valueBuf);
			txn.commit();
			return result;
		}
	}

	override replace(String key, byte[] value) {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")

	}

	override replace(String key, byte[] oldValue, byte[] newValue) {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override clear() {
		try (var txn = env.txnWrite()) {
			db.drop(txn, false);
			txn.commit();
		}
	}

	override containsKey(Object key) {
		var keyByte = (key as String).getBytes(StandardCharsets.UTF_8);
		var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
		keyBuf.put(keyByte).flip
		try (var txn = env.txnRead()) {
			return db.get(txn, keyBuf) !== null;
		}
	}

	override containsValue(Object value) {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override entrySet() {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override get(Object key1) {
		var key = key1 as String
		try(var txn = env.txnRead) {
			var ByteBuffer buffer = null;
			try(var c = db.openCursor(txn)) {
				var keyByte = key.getBytes(StandardCharsets.UTF_8);
				var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
				keyBuf.put(keyByte).flip
				if (c.get(keyBuf, GetOp.MDB_SET_KEY)) {
					buffer = c.^val();
					if (buffer !== null) {
						var int length = buffer.limit() + buffer.position();
						var byte[] bytes = newByteArrayOfSize(length);
						buffer.get(bytes);
						return bytes;
					}
				}
			}
		}
		return null;
	}

	override isEmpty() {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override keySet() {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

	override put(String key, byte[] value) {
		try(var txn = env.txnWrite) {
			val txn1 = txn;
			var keyByte = key.getBytes(StandardCharsets.UTF_8);
			var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
			keyBuf.put(keyByte).flip
			var ByteBuffer valueBuf = ByteBuffer.allocateDirect(value.length);
			valueBuf.put(value).flip
			db.put(txn1, keyBuf, valueBuf);
			txn.commit;
		}
		return value;
	}

	override putAll(Map<? extends String, ? extends byte[]> m) {
		try (var txn = env.txnWrite()) {
			val txn1 = txn;
			m.forEach [ key, value |
				var keyByte = key.getBytes(StandardCharsets.UTF_8);
				var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
				keyBuf.put(keyByte).flip
				var ByteBuffer valueBuf = ByteBuffer.allocateDirect(value.length);
				valueBuf.put(value).flip
				db.put(txn1, keyBuf, valueBuf);
			]
			txn.commit();
		}
	}

	override remove(Object key) {
		var keyByte = (key as String).getBytes(StandardCharsets.UTF_8);
		var ByteBuffer keyBuf = ByteBuffer.allocateDirect(keyByte.length);
		keyBuf.put(keyByte).flip
		try (var txn = env.txnWrite()) {
			db.delete(txn, keyBuf);
			txn.commit();
		}
		return null;
	}

	override size() {
		try(var txn = env.txnRead) {
			var stat = this.db.stat(txn)
			var long pageSize = stat.pageSize
			var branchPages = stat.branchPages
			var leafPages = stat.leafPages
			var long totalBytes = (branchPages + leafPages) * pageSize;
			LOG.info(String.format("Total DB size: %d bytes (%.2f MB)%n", totalBytes, totalBytes / (1024.0 * 1024.0)));
			if (totalBytes <= Integer.MAX_VALUE) {
				return totalBytes as int;
			} else {
				return Integer.MAX_VALUE
			}
		}
	}

	override values() {
		throw new UnsupportedOperationException("TODO: auto-generated method stub")
	}

}
